#include "client.h"
#include "logcat.h"

extern int debug_flag;

int error(char *error) {
    LOGE("%s", error);
    return EXIT_FAILURE;
}

int send_file(int sock, char *new_filename, struct addrinfo *res, char *file_to_send, mode_type mode) {

	char *buff = malloc(PACKET_MAX_LENGTH);
	if (buff == NULL) {
        return error("Could not allocate memory.");
	}

	FILE *f = fopen(file_to_send, "r");
	if (f == NULL) {
        return error("File could not be opened.");
	}

	char m[9];
	if (mode == NETASCII) {
		strncpy(m, "netascii", 8);
		m[8] = '\0';
	} else if (mode == OCTET) {
		strncpy(m, "octet", 5);
		m[5] = '\0';
	}

	set_ushort(buff, WRQ); // set opcode
	int indent = set_char_arr(buff, new_filename, 2, 1); // set filename
	indent = set_char_arr(buff, m, indent, 1); // set mode
	int bytes_to_send = indent - 1;

	if (sendto(sock, buff, bytes_to_send, 0,
	    res->ai_addr, res->ai_addrlen) == -1) { // send request

        return error("Send error.");
	}

	struct pollfd ufd;
	ufd.fd = sock;
	ufd.events = POLLIN;

	char addr[INET_ADDRSTRLEN];

	struct sockaddr_in faddr;
	socklen_t addrsize = sizeof (faddr);

	int transfer_started = 0;
	int retry_count = RETRY_COUNT;
	uint16_t packets_sent = 1;

	int from_previous = 0;

	char *new_buf = malloc(PACKET_MAX_LENGTH * 2 - 4);

	uint16_t port = 0;

	int status = EXIT_FAILURE;

	int n;
	while (1) {
		errno = 0;
		int pol_res = poll(&ufd, 1, POLL_WAIT);
		if (pol_res == 0) {
			if (retry_count == 0) {
				break;
			}
			printf("Message lost, resending, tries: %i\n",
				retry_count);

			if (transfer_started) {
				if (send(sock, buff, bytes_to_send, 0) == -1) {
					perror("Send error.");
					break;
				}
			} else {
				if (sendto(sock, buff, bytes_to_send, 0,
					res->ai_addr, res->ai_addrlen) == -1) {

					perror("Send error.");
					break;
				}
			}
			retry_count--;
			continue;
		}
		retry_count = RETRY_COUNT; // refresh the retry count
		if (transfer_started) {
			if ((n = recv(sock, buff, PACKET_MAX_LENGTH, 0)) > 0) {
				if (debug_flag) {
					fprintf(stderr, "DBG: received %d bytes from %s:%d\n", n, addr, port);
				}

				uint16_t opcode = get_ushort(buff);
				if (opcode == ACK) {
					uint16_t packet_num = get_ushort(buff + 2);

					if (bytes_to_send < PACKET_MAX_LENGTH) { // we've got the ack for the last packet
						status = EXIT_SUCCESS;
						break;
					}

					if (packet_num < packets_sent) { // probably just lost somewhere and we are getting it now, so we are ignoring it
						continue;
					} else if (packet_num > packets_sent) {
						send_error_msg(sock, NOT_DEFINED, "Wrong packet number received.");
						fprintf(stderr, "Expected %i packet, not %i", packets_sent, packet_num);
						break;
					}

					packets_sent++;
					bytes_to_send = send_data(buff, new_buf, sock, &from_previous, f, mode, packets_sent);

				} else if (opcode == ERROR) {
					print_error_msg(buff);
					break;
				} else {
					fprintf(stderr, "Unexpected opcode\n");
					send_error_msg(sock, ILLEGAL_OP, "Unexpected opcode.");
					break;
				}
			} else if (n == -1) {
				perror("Recv error occured.");
				break;
			}
		} else {
			if ((n = recvfrom(sock, buff, PACKET_MAX_LENGTH, 0, (struct sockaddr *)&faddr, &addrsize)) > 0) {
				if (inet_ntop(AF_INET, &faddr.sin_addr, addr, sizeof (addr)) == NULL) {
					send_error_msg(sock, NOT_DEFINED, "Internal server error, try again later.");
					perror("Inet_ntop error.");
					break;
				}
				port = ntohs(faddr.sin_port);
				if (debug_flag) {
					fprintf(stderr, "DBG: received %d bytes from %s:%d\n", n, addr, port);
				}

				uint16_t opcode = get_ushort(buff);
				if (opcode == ACK) {
					uint16_t packet_num = get_ushort(buff + 2);

					transfer_started = 1;
					if (connect(sock, (struct sockaddr *)&faddr, addrsize) == -1) {
						perror("Connect error.");
						break;
					}

					if (packet_num != 0) {
						fprintf(stderr, "Server didn't respond with ack 0. Exiting.\n");
						send_error_msg(sock, NOT_DEFINED, "Expected ack 0.");
						break;
					}

					bytes_to_send = send_data(buff, new_buf, sock, &from_previous, f, mode, packets_sent);

				} else if (opcode == ERROR) {
					print_error_msg(buff);
					break;
				} else {
					fprintf(stderr, "Unexpected opcode\n");
					send_error_msg_to(sock, ILLEGAL_OP, "Unexpected opcode.", (struct sockaddr *)&faddr, addrsize);
					break;
				}
			} else if (n == -1) {
				perror("Recv error occured.");
				break;
			}
		}
	}
	free(new_buf);
	free(buff);
	fclose(f);
	return (status);
}

int read_file(int sock, char * filename, struct addrinfo *res, char * file_to_save, mode_type mode) {
	char * buf = malloc(PACKET_MAX_LENGTH);
	if (buf == NULL) {
        return error("Could not allocate memory, request won't be satisified.");
	}

	FILE *f = fopen(file_to_save, "w");
	if (f == NULL) {
        return error("Could not create file for writing.");
	}

	char m[9];
	if (mode == NETASCII) {
		strncpy(m, "netascii", 8);
		m[8] = '\0';
	} else if (mode == OCTET) {
		strncpy(m, "octet", 5);
		m[5] = '\0';
	}

	LOGE("filename:%s", filename);
	set_ushort(buf, RRQ); // set opcode
	int indent = set_char_arr(buf, filename, 2, 1); // set filename
	indent = set_char_arr(buf, m, indent, 1); // set mode

	int bytes_to_send = indent - 1;
    LOGE("buf:%s", buf);

	if (sendto(sock, buf, bytes_to_send, 0, res->ai_addr, res->ai_addrlen) == -1) { // send request
        return error("Send error.");
	}

	struct pollfd ufd;
	ufd.fd = sock;
	ufd.events = POLLIN;

	char addr[INET_ADDRSTRLEN];

	struct sockaddr_in faddr;
	socklen_t addrsize = sizeof (faddr);

	int retry_count = RETRY_COUNT;
	int transfer_started = 0;

	int last_acknowledged = 0;

	int cr_at_the_end = 0;

	uint16_t port;

	int status = EXIT_FAILURE;

	int n;
	while (1) {
		errno = 0;
		int pol_res = poll(&ufd, 1, POLL_WAIT);
		if (pol_res == 0) {
			if (retry_count == 0) {
				break;
			}
			if (debug_flag) {
				printf("Message lost, resending, tries: %i\n", retry_count);
			}
			if (transfer_started) {
				if (send(sock, buf, bytes_to_send, 0) == -1) {
					perror("Send error.");
					break;
				}
			} else {
				if (sendto(sock, buf, bytes_to_send, 0, res->ai_addr, res->ai_addrlen) == -1) {
					perror("Send error.");
					break;
				}
			}
			retry_count--;
			continue;
		}

		if (transfer_started) {
			if ((n = recv(sock, buf, PACKET_MAX_LENGTH, 0)) > 0) {
				uint16_t opcode = get_ushort(buf);

				if (debug_flag) {
					fprintf(stderr, "DBG: received %d bytes from %s:%d\n", n, addr, port);
				}

				if (opcode == DATA) {
					uint16_t data_packet = get_ushort(buf + 2);

					if (data_packet < last_acknowledged + 1) { // probably just lost somewhere and we are getting it now, so we are ignoring it
						continue;
					} else if (data_packet > last_acknowledged + 1) {
						fprintf(stderr, "Expected %i packet, not %i", last_acknowledged + 1, data_packet);
						break;
					}

					if (mode == OCTET) {
						fwrite(buf + 4, 1, n - 4, f);
					} else if (mode == NETASCII) {
						convert_from_net_and_write(buf, f, &cr_at_the_end, n);
					}

					set_ushort(buf, ACK);
					set_ushort(buf + 2, data_packet);

					if (send(sock, buf, 4, 0) == -1) {
						perror("Send error.");
						break;
					}

					if (n < PACKET_MAX_LENGTH) {
						if (debug_flag) {
							printf("Request completed.\n");
						}
						status = EXIT_SUCCESS;
						break;
					}

					last_acknowledged++;
				} else if (opcode == ERROR) {
					print_error_msg(buf);
					break;
				} else {
					fprintf(stderr, "Unexpected opcode\n");
					send_error_msg_to(sock, ILLEGAL_OP, "Unexpected opcode.", (struct sockaddr *)&faddr, addrsize);
					break;
				}

			} else if (n == -1) {
				perror("Recv error occured.");
				break;
			}
		} else {
			if ((n = recvfrom(sock, buf, PACKET_MAX_LENGTH, 0, (struct sockaddr *)&faddr, &addrsize)) > 0) {
				inet_ntop(AF_INET, &faddr.sin_addr, addr, sizeof (addr));
				port = ntohs(faddr.sin_port);
				if (debug_flag) {
					fprintf(stderr, "DBG: received %d bytes from %s:%d\n", n, addr, port);
				}

				uint16_t opcode = get_ushort(buf);

				if (opcode == DATA) {
					uint16_t data_packet = get_ushort(buf + 2);

					if (data_packet != 1) {
						fprintf(stderr, "Expected data packet 1");
						break;
					}

					if (mode == OCTET) {
						fwrite(buf + 4, 1, n - 4, f);
					} else if (mode == NETASCII) {
						convert_from_net_and_write(buf, f, &cr_at_the_end, n);
					}

					set_ushort(buf, ACK);
					set_ushort(buf + 2, data_packet);

					transfer_started = 1;
					if (connect(sock, (struct sockaddr *)&faddr, addrsize) == -1) {
						perror("Connect error.");
						break;
					}

					if (send(sock, buf, 4, 0) == -1) {
						perror("Send error.");
						break;
					}

					if (n < PACKET_MAX_LENGTH) {
						if (debug_flag) {
							printf("Request completed.\n");
						}
						status = EXIT_SUCCESS;
						break;
					}

					last_acknowledged++;
				} else if (opcode == ERROR) {
					print_error_msg(buf);
					break;
				} else {
					fprintf(stderr, "Unexpected opcode\n");
					send_error_msg_to(sock, ILLEGAL_OP, "Unexpected opcode.", (struct sockaddr *)&faddr, addrsize);
					break;
				}

			} else if (n == -1) {
				perror("Recv error occured.");
				break;
			}
		}
	}

	free(buf);
	fclose(f);
	return (status);
}